﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SignInServices.Common;
using SignInServices.CustomFederation.Entities;
using System.ServiceModel;
using System.Data.Objects;
using System.Net.Mail;
using System.Web.Configuration;
using System.Collections;

namespace SignInServices.CustomFederation
{
    public class SSOCustomFederation:ISSOFederation
    {
        #region ISSOFederation<User> Members


        public void DeleteUser(string userName)
        {
            using (FrameworkEntities c = new FrameworkEntities())
            {
                var instance = c.Users.FirstOrDefault(x => x.UserName.ToLower() == userName.ToLower());
                if (instance != null)
                {
                    c.Users.DeleteObject(instance);
                    c.SaveChanges();
                }
                else
                    throw new FaultException<SSOUserDoesNotExistException>(
                        new SSOUserDoesNotExistException(string.Format("User Name '{0}' does not exist", userName)));
            }
        }

        public bool UserExists(string userName)
        {
            using (FrameworkEntities c = new FrameworkEntities())
            {
                return c.Users.Any(x=>x.UserName.ToLower() == userName.ToLower());
            }
        }

        public bool ChangePassword(string userName, string oldPaassword, string newPassword)
        {
            bool result = false;
            AuthenticatedUser usr = GetUserByName(userName);
            if (usr.Password == SSOHelper.HashPassword(newPassword))
            {
                UpdateUser(userName, SSOHelper.HashPassword(newPassword), usr.Email, usr.IsActive);
                result = true;
            }
            return result;
        }

        public PasswordResetResultType ResetPassword(string emailAddress)
        {
            PasswordResetResultType result = PasswordResetResultType.Successful;
            try
            {
                using (FrameworkEntities c = new FrameworkEntities())
                {
                    // Just to make sure that eSQL really works!!
                    ObjectQuery<User> query = c.CreateQuery<User>(
                        string.Format("select value o from FrameworkEntities.Users as where ToLower(o.Email) == {0}", emailAddress.ToLower()));
                    User userObject = query.FirstOrDefault();
                    if (userObject != null)
                    {
                        string newPassword = SSOHelper.GeneratePlainPassword();
                        SmtpClient smtpClient = new SmtpClient();
                        MailMessage msg = new MailMessage() { Subject = "New Password", Body = newPassword };
                        msg.To.Add(emailAddress);
                        smtpClient.SendAsync(msg, userObject.Id);
                        userObject.Password = SSOHelper.HashPassword(newPassword);
                        c.SaveChanges();
                    }
                    else
                        result = PasswordResetResultType.EmailNotFound;
                }
            }
            catch
            {
                result = PasswordResetResultType.Error;
            }
            return result;
        }

        public string RetrievePassword(string userName)
        {
            throw new NotImplementedException();
        }

        private List<User> getUsers(Func<User,bool> predicate, int index, int count)
        {
            List<User> result;
            using (FrameworkEntities c = new FrameworkEntities())
            {
                c.ContextOptions.ProxyCreationEnabled = false;
                result = c.Users.Where(predicate).ToList();
                result.ForEach(x => c.Detach(x));
            }
            return result;
        }

        public int GetOnlineUsersCount()
        {
            int result = 0;
            using (FrameworkEntities c = new FrameworkEntities())
            {
                result  = c.Tickets.Count();
            }
            return result;
        }

        public AuthenticatedUser GetUserByName(string userName)
        {
            User usr = this.getUsers(x => x.UserName.ToLower() == userName.ToLower(), 1, 1).FirstOrDefault();
            return new AuthenticatedUser (usr.Id){ UserName = usr.UserName, Password = usr.Password, Email = usr.Email, IsActive = usr.IsActive.Value, CreationDate = usr.CreatedDate };
        }

        public AuthenticatedUser GetUserByEmail(string emailAddress)
        {
            User usr = this.getUsers(x => x.Email.ToLower() == emailAddress.ToLower(), 1, 1).FirstOrDefault();
            return new AuthenticatedUser(usr.Id) { UserName = usr.UserName, Password = usr.Password, Email = usr.Email, IsActive = usr.IsActive.Value, CreationDate = usr.CreatedDate };
        }

        public AuthenticatedUser GetUserByID(int id)
        {
            User usr = this.getUsers(x => x.Id == id,1,1).FirstOrDefault();
            return new AuthenticatedUser(usr.Id) { UserName = usr.UserName, Password = usr.Password, Email = usr.Email, IsActive = usr.IsActive.Value, CreationDate = usr.CreatedDate };
        }

        public bool SignIn(string userName, string password)
        {
            bool result = false;
            using (FrameworkEntities c = new FrameworkEntities())
            {
                User user = c.Users.FirstOrDefault(x => (x.UserName.ToLower() == userName.ToLower()));
                if (user != null)
                {
                    try
                    {
                        if (user.Password == SSOHelper.HashPassword(password))
                        {
                            SignOut(userName);
                            Ticket ticket = new Ticket() { LoggedIn = DateTime.Now, TokenID = Guid.NewGuid() };
                            user.Tickets.Add(ticket);
                            c.SaveChanges();
                            result = true;
                        }
                        else
                            throw new FaultException<SSOInvalidPasswordException>(
                                new SSOInvalidPasswordException("The username and password does not match"));
                    }
                    catch (Exception ex)
                    {
                        throw new FaultException<Exception>(ex);
                    }
                }
                else
                    throw new FaultException<SSOUserDoesNotExistException>(
                        new SSOUserDoesNotExistException("The given user name does not exists."));
            }
            return result;
        }

        public void SignOut(string userName)
        {
            using (FrameworkEntities c = new FrameworkEntities())
            {
                User user = c.Users.FirstOrDefault(x => x.UserName.ToLower() == userName.ToLower());
                if (user != null)
                {
                    if (user.Tickets.Any())
                    {
                        foreach (Ticket t in user.Tickets.ToList())
                            c.DeleteObject(t);
                        c.SaveChanges();
                    }
                }
            }
        }

        public bool IsAuthenticated(string userName)
        {
            int timeOutMins = int.Parse( WebConfigurationManager.AppSettings["SessionTimeOutMinutes"]);
            bool result = false;
            using (FrameworkEntities c = new FrameworkEntities())
            {
                User user = c.Users.FirstOrDefault(x => x.UserName.ToLower() == userName.ToLower());
                if (user != null)
                {
                    Ticket tc = user.Tickets.FirstOrDefault();
                    if (tc != null)
                    {
                        if (tc.LoggedIn.AddMinutes(timeOutMins) <= DateTime.Now)
                            result = true;
                    }
                }
                else
                    throw new FaultException<SSOUserDoesNotExistException>(new SSOUserDoesNotExistException("The given user name does not exists."));
            }
            return result;
        }


        public void CreateUser(string userName, string password, string eMail, bool isActive)
        {
            using (FrameworkEntities c = new FrameworkEntities())
            {
                User userInfo = new User();    
                userInfo.CreatedDate = DateTime.Now;
                userInfo.UserName = userName;
                userInfo.Password = SSOHelper.HashPassword(password);
                userInfo.Email = eMail;
                userInfo.IsActive = isActive;
                c.Users.AddObject(userInfo);
                c.SaveChanges();
            }
        }

        public void UpdateUser(string userName, string password, string eMail, bool isActive)
        {
            using (FrameworkEntities c = new FrameworkEntities())
            {
                var user = c.Users.FirstOrDefault(x => x.UserName.ToLower() == userName.ToLower());
                if (user != null)
                {
                    user.Email = eMail;
                    user.IsActive = isActive;
                    user.Password = password;
                    c.SaveChanges();
                }
                else
                    throw new Exception("User not found.");
            }
        }

        IList<AuthenticatedUser> ISSOFederation.FindUsersByEmail(string EmailAddress)
        {
            var matchingUsers = this.getUsers(x => x.Email.ToLower().Contains(EmailAddress.ToLower()), 1, 1);
            return matchingUsers.Select(x => new AuthenticatedUser(x.Id)
            {
                UserName = x.UserName,
                Email = x.Email,
                IsActive = x.IsActive.Value,
                Password = x.Password,
                CreationDate = x.CreatedDate
            }).ToList();
        }

        IList<AuthenticatedUser> ISSOFederation.FindUsersByName(string UserName)
        {
            var matchingUsers = this.getUsers(x => x.UserName.ToLower().Contains(UserName.ToLower()), 1, 1);
            return matchingUsers.Select(x => new AuthenticatedUser(x.Id)
            {
                UserName = x.UserName,
                Email = x.Email,
                IsActive = x.IsActive.Value,
                Password = x.Password,
                CreationDate= x.CreatedDate
            }).ToList();
        }


        public IList<AuthenticatedUser> GetUsers(int index, int count)
        {
           var matchingUsers = this.getUsers(x => x.IsActive == true, index, count);
           return matchingUsers.Select(x => new AuthenticatedUser(x.Id)
           {
               UserName = x.UserName,
               Email = x.Email,
               IsActive = x.IsActive.Value,
               Password = x.Password,
               CreationDate = x.CreatedDate
           }).ToList();
        }

        #endregion
    }
}
